<?php
class numbers
{
	var $precision = 20; // For BCMath, this determines how accurate the numbers will be for like add, subtract, multiply, divide, power, sqrt, etc. 
						 // The higher the number the more accurate it is but it could be slower
	
	var $force_bcmath = 0;
	
	var $use_ipsclass_time = ''; 
	
	// PLEASE NO NOT EDIT THE REST OF THE VARIABLES
	// Main file
	var $ipsclass;
	
	var $LIMIT_32 = 2147483647;
	var $LIMIT_64 = '9223372036854775807';
	
	var $is_64bit;
	var $have_bcmath;
	var $use_bcmath;
	
	var $cache_factorial = array(0 => 1);
	
	function do_number_format($number,$dec = 0, $zero_fill = 0)
	{
		$dec = intval($dec);
		$decimal = $this->ipsclass->vars['decimal_seperator'];
		$d = $dec;
		if(!$zero_fill)
		{
			$dec = 0;
			$number = round($number,$d);
			while($d >= 0)
			{
				if(round($number,$d)==$number)
				{
					$dec = $d;
				}
				$d--;
			}
		}
		if($this->ipsclass->vars['number_format'] != 'none')
		{
			$number = number_format($number , $dec, $decimal, $this->ipsclass->num_format);
		}
		else
		{
			$number = number_format($number , $dec, $decimal, "");
		}
		return $number;
	}
	
	function format_currency($number,$dec_places = 0, $zero_fill = 0)
	{
		if($this->compare($number,0) < 0)
		{
			$num = $this->do_number_format($number,$dec_places,$zero_fill);
			return "-".$this->currency_format(substr($num,1));
		}
		else
		{
			return $this->currency_format($this->do_number_format($number,$dec_places,$zero_fill));
		}
	}
	
	function currency_format($text)
	{
		return $this->ipsclass->vars['market_prefix'].$text.$this->ipsclass->vars['market_suffix'];
	}
	
	function time_relative($new_time,$current_time="")
	{
		if(!$current_time)
		{
			$current_time = time();
		}
		
		$ahead = $new_time - $current_time;

		$return = "";
		if($ahead < 0 || $this->use_ipsclass_time)
		{
			$use_time = $this->use_ipsclass_time ? $this->use_ipsclass_time : 'LONG';
			$return = $this->ipsclass->get_date($new_time,$use_time,0,1);
		}
		elseif($ahead == 0)
		{
			$return = $this->ipsclass->lang['now'];
		}
		elseif($ahead == 1)
		{
			$return = sprintf($this->ipsclass->lang['in_time'],$this->ipsclass->lang['one_second']);
		}
		elseif($ahead <= 60)
		{
			$return = sprintf($this->ipsclass->lang['in_time'],sprintf($this->ipsclass->lang['x_seconds'],$ahead));
		}
		elseif($ahead <= 120)
		{
			$return = sprintf($this->ipsclass->lang['in_time'],$this->ipsclass->lang['one_minute']);
		}
		elseif($ahead <= 3600)
		{
			$return = sprintf($this->ipsclass->lang['in_time'],sprintf($this->ipsclass->lang['x_minutes'],floor($ahead / 60)));
		}
		elseif($ahead <= 7200)
		{
			$return = sprintf($this->ipsclass->lang['in_time'],$this->ipsclass->lang['one_hour']);
		}
		elseif($ahead <= 86400)
		{
			$return = sprintf($this->ipsclass->lang['in_time'],sprintf($this->ipsclass->lang['x_hours'],floor($ahead / 3600)));
		}
		elseif($ahead <= 172800)
		{
			$return = sprintf($this->ipsclass->lang['in_time'],$this->ipsclass->lang['one_day']);
		}
		elseif($ahead <= 604800)
		{
			$return = sprintf($this->ipsclass->lang['in_time'],sprintf($this->ipsclass->lang['x_days'],floor($ahead / 86400)));
		}
		elseif($ahead <= 1209600)
		{
			$return = sprintf($this->ipsclass->lang['in_time'],$this->ipsclass->lang['one_week']);
		}
		else // In many many weeks ahead
		{
			$return = sprintf($this->ipsclass->lang['in_time'],sprintf($this->ipsclass->lang['x_weeks'],floor($ahead / 604800)));
		}
		return $return;
	}
	
	function time_unit($total_time)
	{
		// The times returned are approximates
		if($total_time == 1)
		{
			$return = $this->ipsclass->lang['one_second'];
		}
		elseif($total_time <= 60)
		{
			$return = sprintf($this->ipsclass->lang['x_seconds'],$total_time);
		}
		elseif($total_time <= 120)
		{
			$return = $this->ipsclass->lang['one_minute'];
		}
		elseif($total_time <= 3600)
		{
			$return = sprintf($this->ipsclass->lang['x_minutes'],floor($total_time / 60));
		}
		elseif($total_time <= 7200)
		{
			$return = $this->ipsclass->lang['one_hour'];
		}
		elseif($total_time <= 86400)
		{
			$return = sprintf($this->ipsclass->lang['x_hours'],floor($total_time / 3600));
		}
		elseif($total_time <= 172800)
		{
			$return = $this->ipsclass->lang['one_day'];
		}
		elseif($total_time <= 604800)
		{
			$return = sprintf($this->ipsclass->lang['x_days'],floor($total_time / 86400));
		}
		elseif($total_time <= 1209600)
		{
			$return = $this->ipsclass->lang['one_week'];
		}
		else // In many many weeks ahead
		{
			$return = sprintf($this->ipsclass->lang['x_weeks'],floor($total_time / 604800));
		}
		return $return;
	}

	function time_unit_exact($total_time)
	{
		// The times returned are exact
		// Becare on using this because it may return seconds
		if($total_time % 604800 == 0)
		{
			if($total_time == 604800)
			{
				$return = sprintf($this->ipsclass->lang['x_days'],7);
			}
			else
			{
				$return = sprintf($this->ipsclass->lang['x_weeks'],floor($total_time / 604800));
			}
		}
		elseif($total_time % 86400 == 0)
		{
			if($total_time == 86400)
			{
				$return = sprintf($this->ipsclass->lang['x_hours'],24);
			}
			else
			{
				$return = sprintf($this->ipsclass->lang['x_days'],floor($total_time / 604800));
			}
		}
		elseif($total_time % 3600 == 0)
		{
			if($total_time == 3600)
			{
				$return = sprintf($this->ipsclass->lang['x_minutes'],60);
			}
			else
			{
				$return = sprintf($this->ipsclass->lang['x_hours'],floor($total_time / 3600));
			}
		}
		elseif($total_time % 60 == 0)
		{
			if($total_time == 60)
			{
				$return = sprintf($this->ipsclass->lang['x_seconds'],60);
			}
			else
			{
				$return = sprintf($this->ipsclass->lang['x_minutes'],floor($total_time / 60));
			}
		}
		else //if($total_time % 1 == 0)
		{
			if($total_time == 1)
			{
				$return = $this->ipsclass->lang['one_second'];
			}
			else
			{
				$return = sprintf($this->ipsclass->lang['x_seconds'],$total_time);
			}
		}
		return $return;
	}
	
	function time_left($end_time = 0,$type = 'LONG',$norelative = 0)
	{
		$time = time();
		if($end_time < $time || $norelative)
		{
			return $this->ipsclass->get_date($end_time,$type,$norelative);
		}
		
		return $this->time_string($end_time - $time,$type);
	}
	
	function time_string($time,$type)
	{
		$rtn = array();
		if($time >= 604800)
		{
			if($type == 'TINY' || $type == 'SHORT')
			{
				$rtn[] = floor($time/604800)."w";
			}
			else
			{
				if($time >= 1209600)
				{
					$rtn[] = sprintf($this->ipsclass->lang['x_weeks'],floor($time/604800));
				}
				else
				{
					$rtn[] = $this->ipsclass->lang['one_week'];
				}
			}
			$time = $time % 604800;
		}
		if($time >= 86400)
		{
			if($type == 'TINY' || $type == 'SHORT')
			{
				$rtn[] = floor($time/86400)."d";
			}
			else
			{
				if($time >= 172800)
				{
					$rtn[] = sprintf($this->ipsclass->lang['x_days'],floor($time/86400));
				}
				else
				{
					$rtn[] = $this->ipsclass->lang['one_day'];
				}
			}
			$time = $time % 86400;
		}
		if($time >= 3600)
		{
			if($type == 'TINY' || $type == 'SHORT')
			{
				$rtn[] = floor($time/3600)."h";
			}
			else
			{
				if($time >= 7200)
				{
					$rtn[] = sprintf($this->ipsclass->lang['x_hours'],floor($time/3600));
				}
				else
				{
					$rtn[] = $this->ipsclass->lang['one_hour'];
				}
			}
			$time = $time % 3600;
		}
		if($time)
		{
			if($type == 'TINY' || $type == 'SHORT')
			{
				$rtn[] = floor($time/60)."m";
			}
			else
			{
				if($time >= 120)
				{
					$rtn[] = sprintf($this->ipsclass->lang['x_minutes'],floor($time/60));
				}
				else
				{
					$rtn[] = $this->ipsclass->lang['one_minute'];
				}
			}
			$time = $time % 60;
			if($time && count($rtn) <= 1)
			{
				if($type == 'TINY' || $type == 'SHORT')
				{
					$rtn[] = $time."s";
				}
				else
				{
					if($time >= 2)
					{
						$rtn[] = sprintf($this->ipsclass->lang['x_seconds'],$time);
					}
					else
					{
						$rtn[] = $this->ipsclass->lang['one_second'];
					}
				}
				$left = 0;
			}
		}
		if($time && count($rtn) == 0)
		{
			if($type == 'TINY' || $type == 'SHORT')
			{
				$rtn[] = $left."s";
			}
			else
			{
				if($left >= 2)
				{
					$rtn[] = sprintf($this->ipsclass->lang['x_seconds'],$time);
				}
				else
				{
					$rtn[] = $this->ipsclass->lang['one_second'];
				}
			}
			$time = 0;
		}
		
		if($type == 'TINY' || $type == 'SHORT')
		{
			return implode(" ", $rtn);
		}
		else
		{
			return implode(", ", $rtn);
		}
	}
	
	// At last min, at most max
	// output is [$min,$max] (inclusive)
	function random_number($min = 0,$max = 2147483647,$decimal = 0)
	{
		//$phpversion = phpversion();
		//if($phpversion < '4.1.0')
		//{
		//	mt_srand((double)microtime()*1000000);
		//}

		//if($max - $min > 2147483647)
		//{
		//	return -1;
		//}

		//return mt_rand($min, $max);
		list($usec, $sec) = explode(" ", microtime());
		mt_srand(intval($usec*1000+$usec*1000000000+$sec));
		$rand_dec = $this->divide(mt_rand(0,2147483647),2147483647);
		
		if($decimal)
		{
			$diff = $this->subtract($max,$min);
			$rand = $this->add($min,$this->multiply($rand_dec,$diff));
			return $rand;
		}
		else
		{
			$diff = $this->add($this->subtract($max,$min),1);
			$rand = $this->add($this->subtract($min,.5),$this->multiply($rand_dec,$diff));
			return $this->round($rand);
		}
	}

	function load_checks()
	{
		$this->is_64bit = intval($this->LIMIT_64) === $this->LIMIT_32 || intval($this->LIMIT_64) != $this->LIMIT_64 ? 0 : 1;
		if(function_exists('bcadd') && function_exists('bccomp') && function_exists('bcdiv') && function_exists('bcmul') && function_exists('bcpow') && function_exists('bcsub'))
		{
			$this->have_bcmath = 1;
		}
		else
		{
			$this->have_bcmath = 0;
		}
		if($this->force_bcmath || ($this->have_bcmath && !$this->is_64bit))
		{
			$this->use_bcmath = 1;
		}
		else
		{
			$this->use_bcmath = 0;
		}
	}
	
	function add()
	{
		if($this->use_bcmath)
		{
			$total = '0';
			foreach(func_get_args() as $num)
			{
				$total = bcadd($total,$num,$this->precision);
			}
		}
		else
		{
			$total = 0;
			foreach(func_get_args() as $num)
			{
				$total += $num;
			}
		}
		
		return $this->clean_number($total);
	}
	
	function multiply()
	{
		if($this->use_bcmath)
		{
			$total = '1';
			foreach(func_get_args() as $num)
			{
				$total = bcmul($total,$num,$this->precision);
			}
		}
		else
		{
			$total = 1;
			foreach(func_get_args() as $num)
			{
				$total *= $num;
			}
		}
		
		return $this->clean_number($total);
	}
	
	function subtract($num1, $num2)
	{
		if($this->use_bcmath)
		{
			$total = bcsub($num1,$num2,$this->precision);
		}
		else
		{
			$total = $num1 - $num2;
		}
		
		return $this->clean_number($total);
	}
	
	function divide($num1, $num2)
	{
		if($this->have_bcmath && !$this->is_64bit)
		{
			$total = bcdiv($num1,$num2,$this->precision);
		}
		else
		{
			$total = $num1 / $num2;
		}
		
		return $this->clean_number($total);
	}
	
	function power($base, $exponent)
	{
		if($this->compare($exponent, .5) == 0)
		{
			return $this->sqrt($base);
		}
		if($this->use_bcmath)
		{
			if($this->compare($exponent,0) < 0)
			{
				$exponent = $this->multiply($exponent,-1);
				$neg = true;
			}
			$exp_int = $this->parse_input_number($exponent,'int');
			$exp_dec = $this->subtract($exponent,$exp_int);
			$total = $this->multiply(bcpow($base,$exp_int,$this->precision),pow($base,$exp_dec));
			if($neg)
			{
				$total = $this->divide(1,$total);
			}
		}
		else
		{
			$total = pow($base,$exponent);
		}
		
		return $this->clean_number($total);
	}
	
	function sqrt($number)
	{
		if($this->use_bcmath)
		{
			$total = bcsqrt($number,$this->precision);
		}
		else
		{
			$total = sqrt($number);
		}
		
		return $this->clean_number($total);
	}
	
	function compare($num1, $num2)
	{
		if($this->use_bcmath)
		{
			$total = bccomp($num1,$num2,$this->precision);
		}
		else
		{
			if($num1 > $num2)
			{
				$total = 1;
			}
			elseif($num1 < $num2)
			{
				$total = -1;
			}
			else
			{
				$total = 0;
			}
		}
		
		return $total;
	}
	
	function clean_number($num)
	{
		if(is_string($num))
		{
			if(strstr($num,".") !== FALSE)
			{
				$num = rtrim($num,"0");
				$num = rtrim($num,".");
			}
			return $num;
		}
		elseif(is_float($num))
		{
			if((!$this->is_64bit && $num < $this->LIMIT_32) || ($this->is_64bit && $num < $this->LIMIT_64))
			{
				return floatval($num);
			}
			else
			{
				if($this->use_bcmath)
				{
					return $this->clean_number(bcadd($num,'0',$this->precision));
				}
				else
				{
					return $this->clean_number((string)$num);
				}
			}
		}
		else
		{
			return intval($num);
		}
	}
	
	function min()
	{
		$min = NULL;
		
		foreach(func_get_args() as $num)
		{
			if(is_null($min))
			{
				$min = $num;
			}
			elseif($this->compare($min,$num) > 0)
			{
				$min = $num;
			}
		}
		
		return $min;
	}
	
	function max()
	{
		$max = NULL;
		
		foreach(func_get_args() as $num)
		{
			if(is_null($max))
			{
				$max = $num;
			}
			elseif($this->compare($max,$num) < 0)
			{
				$max = $num;
			}
		}
		
		return $max;
	}
	
	function parse_input_number($number,$type='number')
	{
		if(!is_numeric($number))
		{
			return 0;
		}
		// This should clean up the numbers
		if($this->use_bcmath)
		{
			if($type == 'int')
			{
				return bcadd($number,0,0);
			}
			else
			{
				return $this->add($number);
			}
		}
		else
		{
			if($type == 'float')
			{
				return floatval($number);
			}
			if($type == 'int')
			{
				if($this->is_64bit && $this->compare($number,$this->LIMIT_64) > 0)
				{
					return 0;
				}
				return intval($number);
			}
		}
		return $number;
	}
	
	function round($number,$dec_place=0,$clean=0)
	{
		if($this->use_bcmath)
		{
			$offset = .5;
			if($dec_place)
			{
				$offset = $this->multiply($offset,$this->power(10,$dec_place*-1));
			}
			$num = bcadd($number,$offset,$dec_place);
		}
		else
		{
			$num = round($number,$dec_place);
		}
		if($clean)
		{
			return $this->clean_number($num);
		}
		else
		{
			return $num;
		}
	}
	
	function factorial($num = 0)
	{
		if(isset($this->cache_factorial[$num]))
		{
			return $this->cache_factorial[$num];
		}
		
		$f = end($this->cache_factorial);
		$n = key($this->cache_factorial);
		for($i = $n + 1; $i <= $num; $i++)
		{
			$this->cache_factorial[$i] = $f = $this->multiply($f,$i);
		}
		
		return $this->cache_factorial[$num];
	}
	
	function nPr($n, $r)
	{
		return $this->divide($this->factorial($n),$this->factorial($n-$r));
	}
	
	function nCr($n, $r)
	{
		return $this->divide($this->factorial($n),$this->multiply($this->factorial($r),$this->factorial($n-$r)));
	}
	
	function ordinal_form($i)
	{
		$r = $i % 10;
		if($r == 1)
		{
			if($i % 100 != 11)
			{
				return $i."st";
			}
		}
		elseif($r == 2)
		{
			if($i % 100 != 12)
			{
				return $i."nd";
			}
		}
		elseif($r == 3)
		{
			if($i % 100 != 13)
			{
				return $i."rd";
			}
		}
		return $i."th";
	}
}
?>